Harden GL context-loss recovery and gate dashboard reliability in CI - #5
Merged
Conversation
Context loss now quiesces every source of deferred GPU work (draw RAF, wheel-zoom RAF and pending zoom, view animation, view/re-bin timers) and bumps the request sequence so pre-loss kernel/worker replies cannot mutate the rebuilt context. Streamed append payloads stay canonical while the context is down; restore rebuilds all GPU state from them. A failed restore no longer throws from the event handler: it reports context_restore_failed, stays explicitly failed, and ignores later restore events on the detached canvas. Recovery state is observable via root dataset fcContextState and context_lost/context_restored events. The dependency-free render smoke now forces three loss/restore cycles with queued deferred work, asserts quiescence and pixel-identical rebuilds each cycle, and verifies post-restore interaction. CI gains a dashboard reliability smoke: bench_dashboard 10/20/50 must produce a loss-free, fully nonblank 10-chart row within catastrophic budgets, verified by verify_benchmark_report. Interaction and visual budgets are pinned in the verifier so gates cannot be silently relaxed. Timing regressions beyond 2x stay advisory; beyond 4x fail hard. Visual y-axis checks keep asset rows strict while tolerating Chromium glyph-antialias differences on generated charts. Reformatted examples/demo.ipynb (AST-identical) so repo-wide ruff format passes.
FarhanAliRaza
added a commit
that referenced
this pull request
Jul 15, 2026
- Legend now (re)freezes options and swatch scaling via _attach() when added to an axes, so a legend constructed against one axes but attached to another picks up the host's dpi/rcParams (review #1). - Legend.__init__ warns on handles/labels length mismatch and on handles without a legend entry instead of dropping them silently (review #4). - JS tracks every legend box (not just the primary) so _resize refreshes max-height on extra legends too (review #2). - JS line swatch uses ?? so an explicit lw=0 draws nothing instead of falling back to 1.5px (review #3). - Drop unreachable return in _best_legend_loc (review #5).
FarhanAliRaza
added a commit
that referenced
this pull request
Jul 15, 2026
* Improve pyplot legend and dash parity * Fix extra legend rendering regressions * Address review: legend attach recompute, resize tracking, lw=0 swatch - Legend now (re)freezes options and swatch scaling via _attach() when added to an axes, so a legend constructed against one axes but attached to another picks up the host's dpi/rcParams (review #1). - Legend.__init__ warns on handles/labels length mismatch and on handles without a legend entry instead of dropping them silently (review #4). - JS tracks every legend box (not just the primary) so _resize refreshes max-height on extra legends too (review #2). - JS line swatch uses ?? so an explicit lw=0 draws nothing instead of falling back to 1.5px (review #3). - Drop unreachable return in _best_legend_loc (review #5). --------- Co-authored-by: Alek <alek@reflex.dev>
Alek99
pushed a commit
that referenced
this pull request
Aug 2, 2026
#6 — static PNG export (the feature gap the export-size benchmark surfaced): Figure.to_png() renders the standalone HTML in headless Chromium and screenshots it, so the raster matches the live WebGL chart. Dependency-free — shells out to the Chromium binary (env var / PATH / Playwright-cache discovery), no kaleido-class native package; HTML export still needs nothing. Fluid "100%" sizes fall back to an explicit export size. Missing browser raises a fix-naming error. Verified: html_to_png mechanism produces a 640x400 non-blank PNG from a hand-built spec (no numpy); scripts/png_export_smoke.py gates it in CI (stdlib only) and test_figure.py covers the full Figure.to_png path (pointed at Playwright's Chromium so it runs, not skips). #5 — benchmarks/bench_install.py: best-of-N fresh-interpreter cold import + distribution on-disk size vs the competitor set (methodology §1 rows). stdlib only; unavailable libs reported, never dropped. Size is labeled a lower bound (excludes transitive deps — fresh-venv total stays the runbook gold standard). Wired into the CI benchmark job; plotly-resampler added to the competitor install set. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS5WXAwy3xr5YUH3RH2wzK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
context_restore_failed, stays explicitly failed, and ignores later restore events on the detached canvasroot.dataset.fcContextStateandcontext_lost/context_restored/context_restore_failedchart eventsbench_dashboard.py10/20/50 must produce a loss-free, fully nonblank 10-chart row within catastrophic budgets, enforced byverify_benchmark_report.pyexamples/demo.ipynb(AST-identical) so repo-wideruff format --checkpasses and no longer stops CI before browser testsValidation
cargo build --releasepytest -q: 888 passed (8 new tests, including negative paths for every new gate)ruff check .,ruff format --check .(now clean repo-wide),ty checkscripts/render_smoke_nonumpy.py: 3 recovery cycles, all pixel hashes identical, quiescence and post-restore zoom verifiedscripts/visual_regression_smoke.py: 9 charts, 18 assets, 2 chrome shells OKcomplete(0 losses), 20/50partialwith metrics retained →verify_benchmark_report --kind dashboard-browserpassesNotes for reviewers
The 10-chart smoke gate includes hard timing budgets (
render_ms≤ 5 s). Software-GL render is ~1.8 s on a fast Mac; GitHub's SwiftShader runners are typically 2–4× slower, so watch the first CI runs — the loss-free/nonblank gate is the load-bearing part and is timing-independent, so the budgets can be raised without weakening it if they prove noisy.🤖 Generated with Claude Code